package com.l2qq.common.caffeine;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.jfinal.log.Log;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * Caffeine 缓存
 * @author 杜福忠
 */
public class CaffeineKit {
    private static final Log log = Log.getLog(CaffeineKit.class);
    private static final ConcurrentHashMap<String, Cache> CACHES = new ConcurrentHashMap();
    private static String persistenceBasePath = "data/cache/"; // 默认存储路径
    public static Cache putCache(String cacheName, Cache cache){
        return CACHES.put(cacheName, cache);
    }

    public static Cache getCache(String cacheName){
        return CACHES.get(cacheName);
    }

    public static Cache removeCache(String cacheName){
        return CACHES.remove(cacheName);
    }

    static Cache getOrAddCache(String cacheName) {
        Cache cache = CACHES.get(cacheName);
        if (cache == null) {
            synchronized(CaffeineKit.class) {
                cache = CACHES.get(cacheName);
                if (cache == null) {
                    log.warn("Could not find cache config [" + cacheName + "], using default.");
                    cache = Caffeine.newBuilder().build();
                    CACHES.put(cacheName, cache);
                    log.debug("Cache [" + cacheName + "] started.");
                }
            }
        }
        return cache;
    }

    public static void put(String cacheName, Object key, Object value) {
        if(value != null){
            getOrAddCache(cacheName).put(key, value);
        } else {
            remove(cacheName, key);
        }
    }

    @SuppressWarnings("unchecked")
    public static <T> T get(String cacheName, Object key) {
        return (T) getOrAddCache(cacheName).getIfPresent(key);
    }

    @SuppressWarnings("rawtypes")
    public static List getKeys(String cacheName) {
        return new ArrayList(asMap(cacheName).keySet());
    }

    public static ConcurrentMap asMap(String cacheName) {
        return getOrAddCache(cacheName).asMap();
    }

    public static void remove(String cacheName, Object key) {
        getOrAddCache(cacheName).invalidate(key);
    }

    public static void removeAll(String cacheName) {
        getOrAddCache(cacheName).invalidateAll();
    }

    @SuppressWarnings("unchecked")
    public static <T> T get(String cacheName, Object key, Function mappingFunction) {
        return (T)getOrAddCache(cacheName).get(key, mappingFunction);
    }

    @SuppressWarnings("unchecked")
    public static <T> T get(String cacheName, Object key, Class<? extends Function> dataLoaderClass) {
        try {
            Function dataLoader = dataLoaderClass.getDeclaredConstructor().newInstance();
            return (T)getOrAddCache(cacheName).get(key, dataLoader);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // ==================== 持久化相关方法 ====================

    // 从磁盘加载所有缓存数据
    public static void loadAllFromDisk() {
        File cacheDir = new File(persistenceBasePath);
        if (!cacheDir.exists() || !cacheDir.isDirectory()) {
            return;
        }

        File[] files = cacheDir.listFiles((dir, name) -> name.endsWith(".dat.gz"));
        if (files == null) return;

// 修正文件名处理逻辑
        for (File file : files) {
            String fileName = file.getName();

            // 正确计算：".dat.gz" 长度是7个字符
            int suffixLength = ".dat.gz".length();

            if (fileName.length() <= suffixLength) {
                continue; // 跳过无效文件
            }

            // 获取不含后缀的文件名
            String baseName = fileName.substring(0, fileName.length() - suffixLength);

            // 恢复原始缓存名称
            //String cacheName = baseName.replace("__", ":").replace("_", ".");//感觉这个没意义，可能还会导致回写失败
            try {
                loadCacheFromDisk(baseName);
            } catch (Exception e) {
                log.error("Failed to load cache: " + baseName + " from " + file, e);
            }
        }

    }

    // 加载单个缓存
    private static void loadCacheFromDisk(String cacheName) {
        Path path = Paths.get(persistenceBasePath + safeFileName(cacheName));
        if (!Files.exists(path)) return;

        Cache<Object, Object> cache = getOrAddCache(cacheName);

        try (InputStream fis = Files.newInputStream(path);
             GZIPInputStream gzis = new GZIPInputStream(fis);
             ObjectInputStream ois = new ObjectInputStream(gzis)) {

            List<CacheEntry> entries = (List<CacheEntry>) ois.readObject();

            for (CacheEntry entry : entries) {
                cache.put(entry.key, entry.value);
            }

            log.info("Loaded cache: " + cacheName + " (" + entries.size() + " items)");
            System.out.println(cacheName + "✅ 缓存恢复成功: " + entries.size() + " 条目");
        } catch (Exception e) {
            log.error("Failed to load cache: " + cacheName, e);
            deleteCacheFile(cacheName); // 删除损坏的文件
        }
    }

    // 保存所有缓存到磁盘
    public static void persistAll() {
        CACHES.forEach((cacheName, cache) -> {
            try {
                saveCacheToDisk(cacheName, cache);
            } catch (Exception e) {
                log.error("Failed to persist cache: " + cacheName, e);
            }
        });
    }

    // 单个缓存保存到磁盘
    private static void saveCacheToDisk(String cacheName, Cache cache) {
        Path path = Paths.get(persistenceBasePath + safeFileName(cacheName));

        List<CacheEntry> entries = new ArrayList<>();
        cache.asMap().forEach((key, value) -> {
            // 只保存可序列化的数据
            if (key instanceof Serializable && value instanceof Serializable) {
                entries.add(new CacheEntry(key, value));
            }
        });

        try (OutputStream fos = Files.newOutputStream(path);
             GZIPOutputStream gzos = new GZIPOutputStream(fos);
             ObjectOutputStream oos = new ObjectOutputStream(gzos)) {

            oos.writeObject(entries);
            log.debug("Persisted cache: " + cacheName + " (" + entries.size() + " items)");
            System.out.println("✅ 缓存写入硬盘成功: " + entries.size() + " 条目");
        } catch (IOException e) {
            log.error("Failed to save cache: " + cacheName, e);
        }
    }

    // 删除缓存文件
    private static void deleteCacheFile(String cacheName) {
        try {
            Files.deleteIfExists(Paths.get(persistenceBasePath + safeFileName(cacheName)));
        } catch (IOException e) {
            log.error("Failed to delete cache file: " + cacheName, e);
        }
    }

    // 生成安全的文件名（替换特殊字符）
    private static String safeFileName(String cacheName) {
        return cacheName.replace(":", "__").replace(".", "_") + ".dat.gz";
    }

    // 缓存条目类（优化序列化）
    private static class CacheEntry implements Serializable {
        // 添加序列化ID
        private static final long serialVersionUID = 1L;
        final Object key;
        final Object value;

        CacheEntry(Object key, Object value) {
            this.key = key;
            this.value = value;
        }
    }

    // 设置持久化存储路径
    public static void setPersistenceBasePath(String path) {
        persistenceBasePath = path.endsWith("/") ? path : path + "/";
        ensureDirExists(persistenceBasePath);
    }

    // 确保目录存在
    private static void ensureDirExists(String path) {
        try {
            Path dir = Paths.get(path);
            if (!Files.exists(dir)) {
                Files.createDirectories(dir);
            }
        } catch (IOException e) {
            log.error("Failed to create cache directory: " + path, e);
        }
    }

    //清空所有缓存
    public static void clearAll(){
        CACHES.forEach((cacheName, cache) -> {
            try {
                removeAll(cacheName);
            } catch (Exception e) {
                log.error("Failed to persist cache: " + cacheName, e);
            }
        });
    }

}